The goal of this project is to investigate how partnerships involving multiple top-tier players in the NBA impacts various performance measures and team outcomes. Among the research questions we would like to explore are the following:

[ INSERT RESEARCH QUESTIONS HERE ]

To be able to investigate, we need to pull data from multiple NBA seasons. The script below provides code to create functions that pull traditional stats for every player for a given user-defined season.

Load necessary libraries

library(rvest)
library(dplyr)
library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ readr     2.1.5
✔ ggplot2   3.5.1     ✔ stringr   1.5.1
✔ lubridate 1.9.3     ✔ tibble    3.2.1
✔ purrr     1.0.2     ✔ tidyr     1.3.1
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter()         masks stats::filter()
✖ readr::guess_encoding() masks rvest::guess_encoding()
✖ dplyr::lag()            masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors

Function to get NBA roster for a specified year

get_nba_roster <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)


  # Extract the table containing the player statistics
  roster_table <- webpage %>%
    html_node("table#per_game_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
  roster_table <- roster_table %>%
    filter(Player != "Player")
    return(roster_table)
}

Example usage

year <- 2018  # Specify the year
nba_roster <- get_nba_roster(year)

#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)
NA
#Summary statistics

position_roster<-filter(nba_roster,Pos!="PG" )
position_roster

library(plotly)

# Assuming 'nba_roster' is your data frame
input <- nba_roster[, c('MP', 'PTS', 'Player','Pos')]

# Create the plotly scatter plot
fig <- plot_ly(input, x = ~MP, y = ~PTS, type = 'scatter', mode = 'markers',
               text = ~Player,  # This adds player names on hover
               hoverinfo = 'text', # Ensures that only player names appear on hover
               color = ~Pos,  # Colors points based on position
               marker = list(size = 10))

# Set the plot title and axis labels
fig <- fig %>% layout(title = "Minutes Played vs Points Scored",
                      xaxis = list(title = "Minutes Played", range = c(0, 48)),
                      yaxis = list(title = "Points", range = c(0, 35)))

# Show the plot
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
#Data Visualization for Minutes Played vs Points Scored
input <- nba_roster[, c('MP', 'PTS')]
print(head(input))

# Get the input values.
input <- nba_roster[, c('MP', 'PTS')]

# Plot the chart for cars with
# weight between 1.5 to 4 and
# mileage between 10 and 25.
plot(x = input$MP, y = input$PTS,
    xlab = "Minutes Played",
    ylab = "Points",
    xlim = c(0.0, 48),
    ylim = c(0.0, 35),   
    main = "Minutes Played vs Points Scored"
)

#Data Visualization for Field Goals Attempled vs Field Goals Made
input_2 <- nba_roster[, c('FGA', 'FG')]
print(head(input_2))

# Get the input values.
input_2 <- nba_roster[, c('FGA', 'FG')]

# Plot the chart for players with
# field goal attempts between 0.0 to 25.0 and
b_FG<-max(input_2$FG,na.rm=T)
b_FGA<-max(input_2$FGA,na.rm=T)
# Field Goal Made between 0.0 and 25.0
plot(x = input_2$FGA, y = input_2$FG,
    xlab = "Field Goal Attempts",
    ylab = "Field Goal Made",
    xlim = c(0.0, b_FGA),
    ylim = c(0.0, b_FG),     
    main = "Field Goal Attempt vs Field Goal Made"
)

NA
NA
# Create the data for the chart
A <- c(nba_roster$PTS)
B <- c("PF", "PG", "SF", "C", "SG")

# Plot the bar chart
ggplot(nba_roster, aes(x=Pos, fill=PTS))+  

geom_bar()+  

theme_classic(16)+  

xlab("Position")+  

ylab("Points") 
Warning: The following aesthetics were dropped during statistical transformation: fill.
ℹ This can happen when ggplot fails to infer the correct grouping structure in the data.
ℹ Did you forget to specify a `group` aesthetic or to convert a numerical variable into a factor?

ASSIGNMENT 1: Is the data “clean”? Are there any missing values to be accounted for/addressed? If there are any data quality issues,

Initial thought to change the default character to double given that we have fractioned values. i think the columns should be changed from ” chararcter” to “double”

For players who are missing data

nba_roster<-na.omit(nba_roster)

nba_roster




# Convert specific columns from character to double
# Convert all character columns to double
nba_roster %>%
   mutate(across(G:PTS, as.numeric))
NA
NA
NA

To determine whether a player is “top tier” and should be considered a part of a “Big 3” lineup, other authors have transformed traditional stats to create metrics such as

PRA = POINTS + REBOUNDS + ASSISTS

We will consider advanced statistics such as PLAYER EFFIFIENCY RATING:

PER = (PTS + REB + AST + STL + BLK − Missed FG − Missed FT - TO) /GP

In particular, Value over Replacement (VORP) seems to do a solid job of identifying the best players in the league.

The script below provide code to create functions that pull advanced stats for every player for a given user-defined season.


# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)
  
  # Extract the table containing the advanced player statistics
  advanced_stats_table <- webpage %>%
    html_node("table#advanced_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
 # advanced_stats_table <- advanced_stats_table %>%
  #  filter(Player != "Player")
  
  return(advanced_stats_table)
}

# Example usage
year <- 2018  # Specify the year
nba_advanced_stats <- get_nba_advanced_stats(year)

# Print the first few rows of the advanced stats
head(nba_advanced_stats)
NA
NA

ASSIGNMENT 2: Is the advanced data “clean”? Are there any missing values to be accounted for/addressed? If there are any data quality issues,

cleaning similar to first one

The script below provide code to clean out the quality issues presented in the dataframe

#1 We want to order the athletes name to alphabetical order to clean out the filler headers present

newdataframe<- dataframe[order(dataframe$Player)]
Error: object 'dataframe' not found

#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats <- nba_advanced_stats[order(nba_advanced_stats$Player),]



# remove filler rows that had been previously used as headers on webpage
AO_nba_advanced_stats<- AO_nba_advanced_stats[-c(502:526), ]


#remove na from dataframe
AO_nba_advanced_stats %>% 
  select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-20]
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-24]


#change range of cloumns <dbl> from <chr>

AO_nba_advanced_stats %>%
   mutate(across(G:VORP, as.numeric))
Warning: There were 22 warnings in `mutate()`.
The first warning was:
ℹ In argument: `across(G:VORP, as.numeric)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 21 remaining warnings.

ASSIGNMENT 3: Merge the cleaned up datasets to create one new data frame with the traditional and advanced stats.

#how to merge two files into one new data frame
nba_merge<-merge(nba_roster, AO_nba_advanced_stats, by = c("Rk", "Player", "Pos","Age", "Tm","G"))#, by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
Error in fix.by(by.x, x) : 'by' must specify a uniquely valid column

ASSIGNMENT 4: Make a function with argument year that outputs one dataframe with the merged traditional and advanced data.


combined_nba_stats<-function(year){
get_nba_roster2 <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)


  # Extract the table containing the player statistics
  roster_table <- webpage %>%
    html_node("table#per_game_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
  roster_table <- roster_table %>%
    filter(Player != "Player")
    return(roster_table)
}
  
  year <- 2023  # Specify the year
nba_roster2 <- get_nba_roster2(year)

#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)


#take out the N/A 
nba_roster2<-na.omit(nba_roster2)


# Convert specific columns from character to double

nba_roster2 %>%
   mutate(across(G:PTS, as.numeric))

#ADVANCED STATS

# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)
  
  # Extract the table containing the advanced player statistics
  advanced_stats_table <- webpage %>%
    html_node("table#advanced_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
 # advanced_stats_table <- advanced_stats_table %>%
  #  filter(Player != "Player")
  
  return(advanced_stats_table)
}

# Example usage
year <- 2023  # Specify the year
nba_advanced_stats2<- get_nba_advanced_stats(year)

# Print the first few rows of the advanced stats
head(nba_advanced_stats2)



#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats2<- nba_advanced_stats2[order(nba_advanced_stats2$Player),]





#remove na from dataframe
AO_nba_advanced_stats2 %>% 
  select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-20]
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-24]


# remove filler rows that had been previously used as headers on webpage


AO_nba_advanced_stats2 <- AO_nba_advanced_stats2[AO_nba_advanced_stats2$Player != "Player",]

AO_nba_advanced_stats2$Player <- factor(AO_nba_advanced_stats2$Player)




#change range of cloumns <dbl> from <chr>

AO_nba_advanced_stats2 %>%
   mutate(across(G:VORP, as.numeric))
   
   nba_merge<-merge(nba_roster2, AO_nba_advanced_stats2, by.x = c("Rk", "Player", "Pos","Age", "Tm","G"), by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
   
}

ASSIGNMENT 5: Make this file more visually appealng, with headers, bullet points, sections and subsections as you see fit. You may consider migrating over to Quarto for this reason.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGludmVzdGlnYXRlIGhvdyBwYXJ0bmVyc2hpcHMgaW52b2x2aW5nIG11bHRpcGxlIHRvcC10aWVyIHBsYXllcnMgaW4gdGhlIE5CQSBpbXBhY3RzIHZhcmlvdXMgcGVyZm9ybWFuY2UgbWVhc3VyZXMgYW5kIHRlYW0gb3V0Y29tZXMuIEFtb25nIHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgd2Ugd291bGQgbGlrZSB0byBleHBsb3JlIGFyZSB0aGUgZm9sbG93aW5nOgoKKlsgSU5TRVJUIFJFU0VBUkNIIFFVRVNUSU9OUyBIRVJFIF0qCgpUbyBiZSBhYmxlIHRvIGludmVzdGlnYXRlLCB3ZSBuZWVkIHRvIHB1bGwgZGF0YSBmcm9tIG11bHRpcGxlIE5CQSBzZWFzb25zLiBUaGUgc2NyaXB0IGJlbG93IHByb3ZpZGVzIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgdHJhZGl0aW9uYWwgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgoKTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpGdW5jdGlvbiB0byBnZXQgTkJBIHJvc3RlciBmb3IgYSBzcGVjaWZpZWQgeWVhcgpgYGB7cn0KZ2V0X25iYV9yb3N0ZXIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KYGBgCgpFeGFtcGxlIHVzYWdlCmBgYHtyfQp5ZWFyIDwtIDIwMTggICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyIDwtIGdldF9uYmFfcm9zdGVyKHllYXIpCgojUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByb3N0ZXIKaGVhZChuYmFfcm9zdGVyKQp0YWlsKG5iYV9yb3N0ZXIpCgpgYGAKYGBge3J9CiNTdW1tYXJ5IHN0YXRpc3RpY3MKCnBvc2l0aW9uX3Jvc3RlcjwtZmlsdGVyKG5iYV9yb3N0ZXIsUG9zIT0iUEciICkKcG9zaXRpb25fcm9zdGVyCmBgYAoKCgpgYGB7cn0KCmxpYnJhcnkocGxvdGx5KQoKIyBBc3N1bWluZyAnbmJhX3Jvc3RlcicgaXMgeW91ciBkYXRhIGZyYW1lCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnLCAnUGxheWVyJywnUG9zJyldCgojIENyZWF0ZSB0aGUgcGxvdGx5IHNjYXR0ZXIgcGxvdApmaWcgPC0gcGxvdF9seShpbnB1dCwgeCA9IH5NUCwgeSA9IH5QVFMsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbWFya2VycycsCiAgICAgICAgICAgICAgIHRleHQgPSB+UGxheWVyLCAgIyBUaGlzIGFkZHMgcGxheWVyIG5hbWVzIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGhvdmVyaW5mbyA9ICd0ZXh0JywgIyBFbnN1cmVzIHRoYXQgb25seSBwbGF5ZXIgbmFtZXMgYXBwZWFyIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGNvbG9yID0gflBvcywgICMgQ29sb3JzIHBvaW50cyBiYXNlZCBvbiBwb3NpdGlvbgogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCkpCgojIFNldCB0aGUgcGxvdCB0aXRsZSBhbmQgYXhpcyBsYWJlbHMKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIHZzIFBvaW50cyBTY29yZWQiLAogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIiwgcmFuZ2UgPSBjKDAsIDQ4KSksCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUG9pbnRzIiwgcmFuZ2UgPSBjKDAsIDM1KSkpCgojIFNob3cgdGhlIHBsb3QKZmlnCgoKYGBgCmBgYHtyfQojRGF0YSBWaXN1YWxpemF0aW9uIGZvciBNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnKV0KcHJpbnQoaGVhZChpbnB1dCkpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dCA8LSBuYmFfcm9zdGVyWywgYygnTVAnLCAnUFRTJyldCgojIFBsb3QgdGhlIGNoYXJ0IGZvciBjYXJzIHdpdGgKIyB3ZWlnaHQgYmV0d2VlbiAxLjUgdG8gNCBhbmQKIyBtaWxlYWdlIGJldHdlZW4gMTAgYW5kIDI1LgpwbG90KHggPSBpbnB1dCRNUCwgeSA9IGlucHV0JFBUUywKCXhsYWIgPSAiTWludXRlcyBQbGF5ZWQiLAoJeWxhYiA9ICJQb2ludHMiLAoJeGxpbSA9IGMoMC4wLCA0OCksCgl5bGltID0gYygwLjAsIDM1KSwJIAoJbWFpbiA9ICJNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkIgopCgpgYGAKCgpgYGB7cn0KI0RhdGEgVmlzdWFsaXphdGlvbiBmb3IgRmllbGQgR29hbHMgQXR0ZW1wbGVkIHZzIEZpZWxkIEdvYWxzIE1hZGUKaW5wdXRfMiA8LSBuYmFfcm9zdGVyWywgYygnRkdBJywgJ0ZHJyldCnByaW50KGhlYWQoaW5wdXRfMikpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dF8yIDwtIG5iYV9yb3N0ZXJbLCBjKCdGR0EnLCAnRkcnKV0KCiMgUGxvdCB0aGUgY2hhcnQgZm9yIHBsYXllcnMgd2l0aAojIGZpZWxkIGdvYWwgYXR0ZW1wdHMgYmV0d2VlbiAwLjAgdG8gMjUuMCBhbmQKYl9GRzwtbWF4KGlucHV0XzIkRkcsbmEucm09VCkKYl9GR0E8LW1heChpbnB1dF8yJEZHQSxuYS5ybT1UKQojIEZpZWxkIEdvYWwgTWFkZSBiZXR3ZWVuIDAuMCBhbmQgMjUuMApwbG90KHggPSBpbnB1dF8yJEZHQSwgeSA9IGlucHV0XzIkRkcsCgl4bGFiID0gIkZpZWxkIEdvYWwgQXR0ZW1wdHMiLAoJeWxhYiA9ICJGaWVsZCBHb2FsIE1hZGUiLAoJeGxpbSA9IGMoMC4wLCBiX0ZHQSksCgl5bGltID0gYygwLjAsIGJfRkcpLAkgCgltYWluID0gIkZpZWxkIEdvYWwgQXR0ZW1wdCB2cyBGaWVsZCBHb2FsIE1hZGUiCikKCgpgYGAKCgoKYGBge3J9CiMgQ3JlYXRlIHRoZSBkYXRhIGZvciB0aGUgY2hhcnQKQSA8LSBjKG5iYV9yb3N0ZXIkUFRTKQpCIDwtIGMoIlBGIiwgIlBHIiwgIlNGIiwgIkMiLCAiU0ciKQoKIyBQbG90IHRoZSBiYXIgY2hhcnQKZ2dwbG90KG5iYV9yb3N0ZXIsIGFlcyh4PVBvcywgZmlsbD1QVFMpKSsgIAoKZ2VvbV9iYXIoKSsgIAoKdGhlbWVfY2xhc3NpYygxNikrICAKCnhsYWIoIlBvc2l0aW9uIikrICAKCnlsYWIoIlBvaW50cyIpIAoKYGBgCgoKKipBU1NJR05NRU5UIDE6KiogKklzIHRoZSBkYXRhICJjbGVhbiI/IEFyZSB0aGVyZSBhbnkgbWlzc2luZyB2YWx1ZXMgdG8gYmUgYWNjb3VudGVkIGZvci9hZGRyZXNzZWQ/IElmIHRoZXJlIGFyZSBhbnkgZGF0YSBxdWFsaXR5IGlzc3VlcywqCgogLSAqYS4gcHJvcG9zZSBhIG1ldGhvZCB0byByZXNvbHZlIHRoZW0qCiAgICAgICAKSW5pdGlhbCB0aG91Z2h0IHRvIGNoYW5nZSB0aGUgZGVmYXVsdCBjaGFyYWN0ZXIgdG8gZG91YmxlIGdpdmVuIHRoYXQgd2UgaGF2ZSBmcmFjdGlvbmVkIHZhbHVlcy4gaSB0aGluayB0aGUgY29sdW1ucyBzaG91bGQgYmUgY2hhbmdlZCBmcm9tICIgY2hhcmFyY3RlciIgdG8gImRvdWJsZSIgCgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCnJlbW92aW5nIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgZGF0YSBmcm9tIHRoZSBkYXRhc2V0LCB1c2luZyB0aGUgZnVuY3Rpb24gIm5hLm9taXQiIHdoaWNoIHdpbGwgcmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBmcm9tIG91ciBkYXRhc2V0CgoKIC0gKmMuIGltcGxlbWVudCB5b3VyIHByb3Bvc2VkIGNoYW5nZXMqCgoKRm9yIHBsYXllcnMgd2hvIGFyZSBtaXNzaW5nIGRhdGEKYGBge3J9Cm5iYV9yb3N0ZXI8LW5hLm9taXQobmJhX3Jvc3RlcikKCm5iYV9yb3N0ZXIKCgoKCiMgQ29udmVydCBzcGVjaWZpYyBjb2x1bW5zIGZyb20gY2hhcmFjdGVyIHRvIGRvdWJsZQojIENvbnZlcnQgYWxsIGNoYXJhY3RlciBjb2x1bW5zIHRvIGRvdWJsZQpuYmFfcm9zdGVyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6UFRTLCBhcy5udW1lcmljKSkKCgoKYGBgCgpUbyBkZXRlcm1pbmUgd2hldGhlciBhIHBsYXllciBpcyAidG9wIHRpZXIiIGFuZCBzaG91bGQgYmUgY29uc2lkZXJlZCBhIHBhcnQgb2YgYSAiQmlnIDMiIGxpbmV1cCwgb3RoZXIgYXV0aG9ycyBoYXZlIHRyYW5zZm9ybWVkIHRyYWRpdGlvbmFsIHN0YXRzIHRvIGNyZWF0ZSBtZXRyaWNzIHN1Y2ggYXMKClBSQSA9IFBPSU5UUyArIFJFQk9VTkRTICsgQVNTSVNUUyAKCldlIHdpbGwgY29uc2lkZXIgYWR2YW5jZWQgc3RhdGlzdGljcyBzdWNoIGFzIFBMQVlFUiBFRkZJRklFTkNZIFJBVElORzoKClBFUiA9IChQVFMgKyBSRUIgKyBBU1QgKyBTVEwgKyBCTEsg4oiSIE1pc3NlZCBGRyDiiJIgTWlzc2VkIEZUIC0gVE8pIC9HUAoKSW4gcGFydGljdWxhciwgVmFsdWUgb3ZlciBSZXBsYWNlbWVudCAoVk9SUCkgc2VlbXMgdG8gZG8gYSBzb2xpZCBqb2Igb2YgaWRlbnRpZnlpbmcgdGhlIGJlc3QgcGxheWVycyBpbiB0aGUgbGVhZ3VlLgoKVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgYWR2YW5jZWQgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgpgYGB7cn0KCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDE4ICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0cykKCgpgYGAKCioqQVNTSUdOTUVOVCAyOioqICpJcyB0aGUgYWR2YW5jZWQgZGF0YSAiY2xlYW4iPyBBcmUgdGhlcmUgYW55IG1pc3NpbmcgdmFsdWVzIHRvIGJlIGFjY291bnRlZCBmb3IvYWRkcmVzc2VkPyBJZiB0aGVyZSBhcmUgYW55IGRhdGEgcXVhbGl0eSBpc3N1ZXMsKgoKIC0gKmEuIHByb3Bvc2UgYSBtZXRob2QgdG8gcmVzb2x2ZSB0aGVtKgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCgogLSAqYy4gaW1wbGVtZW50IHlvdXIgcHJvcG9zZWQgY2hhbmdlcyoKIAogY2xlYW5pbmcgc2ltaWxhciB0byBmaXJzdCBvbmUgCiAKIAogVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY2xlYW4gb3V0IHRoZSBxdWFsaXR5IGlzc3VlcyBwcmVzZW50ZWQgaW4gdGhlIGRhdGFmcmFtZQogCiAKIApgYGB7cn0KIzEgV2Ugd2FudCB0byBvcmRlciB0aGUgYXRobGV0ZXMgbmFtZSB0byBhbHBoYWJldGljYWwgb3JkZXIgdG8gY2xlYW4gb3V0IHRoZSBmaWxsZXIgaGVhZGVycyBwcmVzZW50CgpuZXdkYXRhZnJhbWU8LSBkYXRhZnJhbWVbb3JkZXIoZGF0YWZyYW1lJFBsYXllcildCgojMiBOb3cgd2Ugd2FudCB0byByZW1vdmUgdGhlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gdXNlZCBhcyBoZWFkZXJzIG9uIHRoZSB3ZWJwYWdlCgpuZXdkYXRhLmZyYW1lPC1kYXRhZnJhbWVbLWMoNTAyOjUyNiksIF0KCiMzIG5vdyB3ZSB3YW50IHRvIHJlbW92ZSBhbGwgdGhlIE4vQXMgZnJvbSB0aGUgZGF0YXNldApkYXRhZnJhbWUgJT4lIAogIHNlbGVjdCh3aGVyZSh+IWFsbChpcy5uYSguKSkpKQpgYGAKIAogCiAKYGBge3J9Cgojd2FudCB0byBvcmRlciBieSBhbHBoYWJldGljIG5hbWUgdG8gbWFrZSBjbGVhbmluZyBvdXQgdGhlIGZpbGxlciBoZWFkZXJzIGZyb20gdGhlIGRhdGFzZXQKQU9fbmJhX2FkdmFuY2VkX3N0YXRzIDwtIG5iYV9hZHZhbmNlZF9zdGF0c1tvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMkUGxheWVyKSxdCgoKCiMgcmVtb3ZlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gcHJldmlvdXNseSB1c2VkIGFzIGhlYWRlcnMgb24gd2VicGFnZQpBT19uYmFfYWR2YW5jZWRfc3RhdHM8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHNbLWMoNTAyOjUyNiksIF0KCgojcmVtb3ZlIG5hIGZyb20gZGF0YWZyYW1lCkFPX25iYV9hZHZhbmNlZF9zdGF0cyAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yMF0KQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yNF0KCgojY2hhbmdlIHJhbmdlIG9mIGNsb3VtbnMgPGRibD4gZnJvbSA8Y2hyPgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCgoKCgpgYGAKCgoqKkFTU0lHTk1FTlQgMzoqKiAqTWVyZ2UgdGhlIGNsZWFuZWQgdXAgZGF0YXNldHMgdG8gY3JlYXRlIG9uZSBuZXcgZGF0YSBmcmFtZSB3aXRoIHRoZSB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgc3RhdHMuKgoKCgpgYGB7cn0KI2hvdyB0byBtZXJnZSB0d28gZmlsZXMgaW50byBvbmUgbmV3IGRhdGEgZnJhbWUKbmJhX21lcmdlPC1tZXJnZShuYmFfcm9zdGVyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMsIGJ5LnggPSBjKCJSayIsICJQbGF5ZXIiLCAiUG9zIiwiQWdlIiwgIlRtIiwiRyIpLCBieS55ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSAsIGFsbC54ID0gVFJVRSwgYWxsLnkgPSBUUlVFKQoKCmhlYWQobmJhX21lcmdlKQpgYGAKCgoqKkFTU0lHTk1FTlQgNDoqKiAqTWFrZSBhIGZ1bmN0aW9uIHdpdGggYXJndW1lbnQgYHllYXJgIHRoYXQgb3V0cHV0cyBvbmUgZGF0YWZyYW1lIHdpdGggdGhlIG1lcmdlZCB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgZGF0YS4qIAoKYGBge3J9Cgpjb21iaW5lZF9uYmFfc3RhdHM8LWZ1bmN0aW9uKHllYXIpewpnZXRfbmJhX3Jvc3RlcjIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KICAKICB5ZWFyIDwtIDIwMjMgICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyMiA8LSBnZXRfbmJhX3Jvc3RlcjIoeWVhcikKCiNQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIHJvc3RlcgpoZWFkKG5iYV9yb3N0ZXIpCnRhaWwobmJhX3Jvc3RlcikKCgojdGFrZSBvdXQgdGhlIE4vQSAKbmJhX3Jvc3RlcjI8LW5hLm9taXQobmJhX3Jvc3RlcjIpCgoKIyBDb252ZXJ0IHNwZWNpZmljIGNvbHVtbnMgZnJvbSBjaGFyYWN0ZXIgdG8gZG91YmxlCgpuYmFfcm9zdGVyMiAlPiUKICAgbXV0YXRlKGFjcm9zcyhHOlBUUywgYXMubnVtZXJpYykpCgojQURWQU5DRUQgU1RBVFMKCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDIzICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzMjwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0czIpCgoKCiN3YW50IHRvIG9yZGVyIGJ5IGFscGhhYmV0aWMgbmFtZSB0byBtYWtlIGNsZWFuaW5nIG91dCB0aGUgZmlsbGVyIGhlYWRlcnMgZnJvbSB0aGUgZGF0YXNldApBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC0gbmJhX2FkdmFuY2VkX3N0YXRzMltvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllciksXQoKCgoKCiNyZW1vdmUgbmEgZnJvbSBkYXRhZnJhbWUKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMjwtQU9fbmJhX2FkdmFuY2VkX3N0YXRzMlssLTIwXQpBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC1BT19uYmFfYWR2YW5jZWRfc3RhdHMyWywtMjRdCgoKIyByZW1vdmUgZmlsbGVyIHJvd3MgdGhhdCBoYWQgYmVlbiBwcmV2aW91c2x5IHVzZWQgYXMgaGVhZGVycyBvbiB3ZWJwYWdlCgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiA8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHMyW0FPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyICE9ICJQbGF5ZXIiLF0KCkFPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyIDwtIGZhY3RvcihBT19uYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllcikKCgoKCiNjaGFuZ2UgcmFuZ2Ugb2YgY2xvdW1ucyA8ZGJsPiBmcm9tIDxjaHI+CgpBT19uYmFfYWR2YW5jZWRfc3RhdHMyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCiAgIAogICBuYmFfbWVyZ2U8LW1lcmdlKG5iYV9yb3N0ZXIyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMyLCBieS54ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSwgYnkueSA9IGMoIlJrIiwgIlBsYXllciIsICJQb3MiLCJBZ2UiLCAiVG0iLCJHIikgLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gVFJVRSkKICAgCn0KCgoKYGBgCgoKKipBU1NJR05NRU5UIDU6KiogKk1ha2UgdGhpcyBmaWxlIG1vcmUgdmlzdWFsbHkgYXBwZWFsbmcsIHdpdGggaGVhZGVycywgYnVsbGV0IHBvaW50cywgc2VjdGlvbnMgYW5kIHN1YnNlY3Rpb25zIGFzIHlvdSBzZWUgZml0LiBZb3UgbWF5IGNvbnNpZGVyIG1pZ3JhdGluZyBvdmVyIHRvIFF1YXJ0byBmb3IgdGhpcyByZWFzb24uKgoKCg==